﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Configuration;
using System.Web.Security;
using System.Security.Principal;
using System.Threading;
using System.Collections;

namespace KeanuMvc.Reference.Routings {

    public class HandlerToController {

        private Assembly webAssembly = null;
        private object objinstant = null;

        public Assembly WebAssembly {
            set {
                webAssembly = value;
            }
        }

        public void HandlerThisPath(Assembly currentAssembly, object Requestpath, ref Dictionary<string, string> UrlMappingCache, bool CheckUser, string[] RoleToCompare, string UnauthorizePagePath) {
            webAssembly = currentAssembly;
            HandlerThisPath(Requestpath, ref UrlMappingCache, CheckUser, RoleToCompare, UnauthorizePagePath);
        }

        private void HandlerThisPath(object Requestpath, ref Dictionary<string, string> UrlMappingCache, bool CheckUser, string[] RoleToCompare, string UnauthorizePagePath) {
            try {
                string sClassName = "home.default";
                string sMethodName = "index";
                string sShortAssemblyName = webAssembly.FullName.ToLower().Split(',')[0];
                string[] aPath = null;
                string sPath = Requestpath.ToString().Remove(0, 1);

                if (sPath == string.Empty) {
                    //Default
                    ExecuteClassAndInvokeMethod(sClassName, sMethodName, CheckUser, RoleToCompare, UnauthorizePagePath);
                    return;
                }

                aPath = sPath.ToString().Split('/');
                List<string> listPath = new List<string>();

                for (int i = 0; i < aPath.Length; i++) if (aPath[i] != string.Empty) listPath.Add(aPath[i]);

                sClassName = String.Join(".", listPath);

                bool IsDirectory = Directory.Exists(HttpContext.Current.Request.PhysicalApplicationPath + "Controllers" + "\\" + String.Join("\\", listPath));

                if (IsDirectory) sClassName = String.Join(".", listPath) + ".default";

                if (ExecuteClassAndInvokeMethod(sClassName, sMethodName, CheckUser, RoleToCompare, UnauthorizePagePath)) return;

                int iLastIndexPath = listPath.Count - 1;
                sMethodName = listPath[iLastIndexPath];
                listPath.RemoveAt(iLastIndexPath);

                sClassName = String.Join(".", listPath);
                IsDirectory = Directory.Exists(HttpContext.Current.Request.PhysicalApplicationPath + "Controllers" + "\\" + String.Join("\\", listPath));
                if (IsDirectory) sClassName = sClassName + ".default";
                if (ExecuteClassAndInvokeMethod(sClassName, sMethodName, CheckUser, RoleToCompare, UnauthorizePagePath)) return;

            } catch (Exception ex) {
                throw ex;
            } finally {
                if (objinstant != null) objinstant = null;
            }
        }

        private bool ExecuteClassAndInvokeMethod(string pClassName, string pMethodName, bool CheckUser, string[] RoleToCompare, string UnauthorizePagePath) {
            try {
                Type[] pTypes = webAssembly.GetTypes();
                MethodInfo[] methodInfos = null;
                MethodInfo methodInfo = null;
                string sShortAssemblyName = webAssembly.FullName.ToLower().Split(',')[0];

                Type type = Array.Find(pTypes, element => element.FullName.ToLower() == sShortAssemblyName + ".controllers." + pClassName);

                if (objinstant == null) objinstant = Activator.CreateInstance(type);

                methodInfos = objinstant.GetType().GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);
                methodInfo = Array.Find(methodInfos, element => element.Name.ToLower() == pMethodName);

                if (CheckUser && RoleToCompare != null) ValidateUserTryInvokeMethod(methodInfo, RoleToCompare, UnauthorizePagePath);

                try {
                    type.InvokeMember(methodInfo.Name, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public, null, objinstant, null);
                } catch (TargetInvocationException exReflection) {
                    throw exReflection;
                }
                if (objinstant != null) objinstant = null;
            } catch {
                return false;
            }

            return true;
        }

        public void SetPrincipal(string UserId, string[] UserRole, string LogoutPagePath, string LogoutPageAjaxPath, string LoginPagePath) {

            if (UserId == string.Empty) {
                if (!HttpContext.Current.Request.RawUrl.ToLower().Contains(LogoutPagePath) &&
                    !HttpContext.Current.Request.RawUrl.ToLower().Contains(LogoutPageAjaxPath) &&
                    !HttpContext.Current.Request.RawUrl.ToLower().Contains(LoginPagePath)
                ) {
                    if (HttpContext.Current.Request.RawUrl.ToLower() == "/") {
                        HttpContext.Current.Response.Redirect(LoginPagePath);
                        return;
                    }
                    if ((HttpContext.Current.Request["X-Requested-With"] == "XMLHttpRequest") || ((HttpContext.Current.Request.Headers != null) && (HttpContext.Current.Request.Headers["X-Requested-With"] == "XMLHttpRequest"))) {
                        HttpContext.Current.Response.Redirect(LogoutPageAjaxPath);
                    } else {
                        HttpContext.Current.Response.Redirect(LogoutPagePath);
                    }

                }
                return;
            }

            GenericIdentity userIdentity = new GenericIdentity(UserId);
            GenericPrincipal userPrincipal = new GenericPrincipal(userIdentity, UserRole);
            HttpContext.Current.User = userPrincipal;
            Thread.CurrentPrincipal = HttpContext.Current.User;
        }

        private void ValidateUserTryInvokeMethod(MethodInfo methodInfo, string[] RoleActionToCompare, string UnauthorizePagePath) {
            if (RoleActionToCompare == null) return;
            object[] customAttributes = methodInfo.GetCustomAttributes(false);
            object objValAttribute = Array.Find(customAttributes, e => e.GetType().Name.Contains("Auth"));

            if (objValAttribute == null) {
                HttpContext.Current.Response.Redirect(UnauthorizePagePath);
            }

            if (objValAttribute.GetType().GetProperty("RoleAction").GetValue(objValAttribute, null).ToString() == "all") {
                return;
            }

            if (Array.IndexOf(RoleActionToCompare, objValAttribute.GetType().GetProperty("RoleAction").GetValue(objValAttribute, null).ToString()) < 0) {
                HttpContext.Current.Response.Redirect(UnauthorizePagePath);
            }

        }

        private void MappingPath() {
            //if (type == null) { /// Try to find (path is not real path may be set in method attribute)
            //    KeyValuePair<string, string> valuePath = new KeyValuePair<string, string>();
            //    try {
            //        valuePath = UrlMappingCache.Where(i => i.Key == sPath.ToString()).First();
            //    } catch { }

            //    if (valuePath.Value != null) {
            //        className = valuePath.Value.Split('|')[0];
            //        strMethodName = valuePath.Value.Split('|')[1];
            //    } else {
            //        className = string.Empty;
            //        MethodInfo[] methods = types
            //            .SelectMany(t => t.GetMethods())
            //            .Where(m => m.GetCustomAttributes(false).Length > 0 && m.ReturnType.Name == "Void" && m.DeclaringType.FullName.Contains(sShortAssemblyName))
            //            .ToArray();

            //        for (int r = 0; r < methods.Length; r++) {
            //            object[] customAttributes = methods[r].GetCustomAttributes(false);
            //            object objValAttribute = Array.Find(customAttributes, e => e.GetType().Name.Contains("MappingPath"));

            //            if (objValAttribute != null) {
            //                if (objValAttribute.GetType().GetProperty("Path").GetValue(objValAttribute, null).ToString().Contains(sPath.ToString())) {
            //                    className = methods[r].DeclaringType.FullName.ToLower();
            //                    strMethodName = methods[r].Name.ToLower();
            //                    UrlMappingCache.Add(sPath.ToString(), className + "|" + strMethodName);
            //                    break;
            //                }
            //            }
            //        }
            //    }
            //    type = Array.Find(types, element => element.FullName.ToLower() == className);

            //}
        }

    }



    [AttributeUsage(AttributeTargets.Method)]
    public class MappingPath : Attribute {

        public string Path { get; set; }

    }

}
